import { _decorator, Vec3, Node, RigidBody, ERigidBodyType } from 'cc';
import { Game, ActionStage, AimType } from '../core/Game';
import { BaseComponent } from './BaseComponent';
import { BallAttribute, BallColor } from '../core/BallAttribute';
import { TableAtrribute } from '../core/TableAttribute';
import { EventManager } from '../core/EventManager';
import { BallGameFactory } from '../category/BallGameFactory';
import { ColliderMask, WatchViewType } from '../core/CommUtil';
import { BallSphere } from './BallSphere';
import { UniformFunction } from '../platform/UniformFunction';
import { SoundManager } from '../framework/SoundManager';
import { TableController } from './TableController';
const { ccclass, property } = _decorator;

@ccclass('BilliardGame')
export class BilliardGame extends BaseComponent {
  public static ColliderBallMask = ColliderMask.BALL + ColliderMask.DESKEDGE + ColliderMask.DESKHOLE;
  private static instance = null as unknown as BilliardGame;

  public hasSpinHitBall = false;
  private currentHitBall: Node = null;
  private currentHitPosition: Vec3 = new Vec3();
  public aimType: AimType = new AimType();
  // 当前步骤击球的信息
  public actionStage = new ActionStage();
  public hasCollisioned = false;
  public hasMainBallHoled = false;
  // 白球第1个碰到的
  public firstCollideBall: BallAttribute = null;
  // 碰撞后的反射线
  private reflectVector: Vec3 = new Vec3();
  // 碰撞后的折射线(目标球的运行线路)
  private refractVector: Vec3 = new Vec3();

  // 视角
  public watchViewType = WatchViewType.View3D;
  // 不同类型的桌x球游戏
  public gameBall = null;

  // 是否有违规击球
  public isIllegalHitBall = false;
  public illegaMessage = '对手开球击球';

  // 当前对局下-当前击球的帧ID
  public currentFrameSeq = 0;
  public currentTimestamp = 0;
  // 当前一轮击球是否开始
  public static Instance() {
    return BilliardGame.instance;
  }
  
  public onLoad() {
    BilliardGame.instance = this;
  }
  public start() {
    this.watchViewType = WatchViewType.View3D;
  }
  public is3DView() {return this.watchViewType === WatchViewType.View3D; }
  public currentHitGanCount() { return this.gameBall.currentHitGanCount(); }
  public initSyncFrame() {
    this.currentFrameSeq = 0;
    this.currentTimestamp = Date.now();
  }
  public clearIllegalInfo() {
    this.isIllegalHitBall = false;
    this.illegaMessage = '正常开球';
  }
  public isFirstStep() {
    return Game.Instance().currentStep <= 0;
  }
  public exchangeUser() {
    console.log('current user finished hit ', Game.Instance().getCurrentPlayUserColor());
    Game.Instance().exchangeUser();
    EventManager.Instance().Emit('ExchangedUserHitBall', Game.Instance().side);
    if (Game.Instance().isSelftHitBall()) {
      new UniformFunction().vibrateShake();
    }
  }
  public robotRenShu() {
    return this.gameBall.robotRenShu(Game.Instance().playUsers);
  }
  // TODO: 先禁止掉 不是很合适
  public isForbidTargetBall() {
    return false;
    if (!this.currentHitBall) {
      return false;
    }
    return !this.gameBall.isTargetHitBall(Game.Instance().getCurrentPlayUserColor(), this.currentHitBall.name);
  }
  public penalyUserScore() {
    const sc = this.gameBall.penalyScore();
    Game.Instance().penaltyCurrentUserScore(sc);
    EventManager.Instance().Emit('AddUserScore', { side: 1 - Game.Instance().side, score: sc });
  }
  public updateUserScore() {
    const sc = this.gameBall.getCurrentStepScore(
      Game.Instance().hitBallList,
      Game.Instance().currentStep,
      Game.Instance().getCurrentPlayUserColor(),
    );
    EventManager.Instance().Emit('AddUserScore', { side: Game.Instance().side, score: sc });
    Game.Instance().updateCurrentUserScore(sc);

    const hitLevel = this.gameBall.isExcellentLevel(
      Game.Instance().hitBallList,
      Game.Instance().currentStep,
      Game.Instance().getCurrentPlayUserColor(),
      Game.Instance().currentUserId(),
    );
    if (hitLevel > 0 && Game.Instance().isSelftHitBall()) {
      SoundManager.Instance().playApplaud();
    }
  }
  public leaveRoomPage() {
    this.node.getParent().active = false;
  }
  public resetBallPosition(orderNums: number[]) {
    if (this.gameBall) {
      this.gameBall.resetBallPosition(orderNums);
    }
  }
  // 开始游戏
  public startGame() {
    console.log('start game billiard ', this);
    this.node.active = true;
    BilliardGame.Instance().actionStage.initStage();
    // 不同类型的球，有不同的规则
    this.gameBall = BallGameFactory.newBallGame(
      Game.Instance().battleCate, Game.Instance().gameType, this.node
    );

    this.gameBall.initBallsPosition();
    this.sleepAllBalls();

    if (Game.Instance().isSelftHitBall()) {
      this.showAllOperation();
    } else {
      this.hideAllOperation();
    }
    this.hasSpinHitBall = false;
    this.firstCollideBall = null;
    this.hasCollisioned = false;
    this.hasMainBallHoled = false;
    this.firstCollideBall = null;
    this.clearIllegalInfo();
  }
  public endGame() {
    this.node.active = false;
  }
  public defaultAimBallPosition() {
    // 不同类型的球，有不同的规则
    const pos = this.gameBall.defaultAimBallPosition();
    EventManager.Instance().Emit('HitBallHasChoosed', pos);
  }
  // 开始下一步击球
  public startStep() {
    this.hasCollisioned = false;
    this.hasMainBallHoled = false;
    this.firstCollideBall = null;
    this.hasSpinHitBall = false;
  }
  public hasHoledScore() {
    return this.gameBall.hasHoledScore(
      Game.Instance().hitBallList,
      Game.Instance().currentStep,
      Game.Instance().getCurrentPlayUserColor(),
    );
  }
  // 校验当前游戏是否结束
  public checkGameEnd() {
    if (this.gameBall.checkGameEnd()) {
      if (this.gameBall.isCurrentUserWin(Game.Instance().side, this.isIllegalHitBall)) {
        Game.Instance().currentUserWin();
      } else {
        Game.Instance().currentUserLose();
      }
      EventManager.Instance().Emit('EndGame', {});
      return true;
    }
    return false;
  }
  public checkIllegalHitBall() {
    const res = this.gameBall.isIllegalHitBall(
      this.firstCollideBall,
      Game.Instance().hitBallList,
      Game.Instance().currentStep,
      Game.Instance().getCurrentPlayUserColor(),
    );
    if (res.isIlleagel) {
      this.isIllegalHitBall = res.isIlleagel;
      this.illegaMessage = res.message;
    } else {
      this.isIllegalHitBall = false;
      this.illegaMessage = '正常交换击球';
    }
  }
  // 检查是否需要将进洞的球拿出来重置 
  public checkOutHoleBalls(isIgnoreColorStage: boolean) {
    const balls = Game.Instance().hitBallList.filter(cur => cur.step === Game.Instance().currentStep);
    for (const ball of balls) {
      if (ball.ba.isMainWhiteBall()) {
        EventManager.Instance().Emit("ResetMainBallPosition", this.getWhiteBallPosition());
      }
    }
    this.gameBall.checkOutHoleBalls(isIgnoreColorStage, this.isIllegalHitBall, balls);
  }
  public isFreeKickBall() {
    return this.isIllegalHitBall;
  }
  // 自由球, 白球复位
  public freeKickBall() {
    EventManager.Instance().Emit("ResetMainBallPosition", this.getWhiteBallPosition());
  }
  public getHitProtectBalls() {
    return this.gameBall.getHitProtectBalls(Game.Instance().getCurrentPlayUserColor());
  }
  // 交换当前用户击球的颜色
  public swapCurrentUserColor(hasExchangeUser: boolean) {
    this.gameBall.swapCurrentUserColor(hasExchangeUser);
  }
  public getUserHitColorText(color: BallColor) {
    if (this.gameBall) {
      return this.gameBall.getUserHitColorText(color);
    }
  }
  // 根据游戏类型返回
  public getActiveBalls() {
    return this.gameBall.getActiveBalls();
  }
  // 网络上传递所有球的位置，包括白球
  public getAllBallList() {
    return this.gameBall.getAllBallList();
  }
  public getAllBallNodeList() {
    return this.gameBall.getAllBallNodeList();
  }
  // 根据球的名称返回球节点
  public getBallNodeByName(name: string) {
    const sballs = this.node.getChildByName('SnookerBalls').children;
    for (const b of sballs) {
      if (b.name === name) return b;
    }
    const balls = this.node.getChildByName('Balls').children;
    for (const b of balls) {
      if (b.name === name) return b;
    }
    return null;
  }
  public getVirtualHitBall() {
    const ball = this.node.getChildByName('VirtualHitBall').getChildByName('BallSphere');
    return ball;
  }
  public getMainWhiteBall() {
    return this.node.getChildByName('MainBall').getChildByName('WhiteBall').getChildByName('BallSphereMain');
  }
  public getDeskBallHolePosition() {
    const tableController = this.node.getChildByName('TableController').getComponent(TableController);
    return tableController.getDeskBallHolePosition();
  }
  public getDeskHolePosition() {
    const tableController = this.node.getChildByName('TableController').getComponent(TableController);
    return tableController.getDeskHolePosition();
  }
  public getDeskExtractInHolePosition() {
    const r = 1.35;
    const holes = this.getDeskBallHolePosition();
    const inholes: Array<Vec3> = [];
    inholes.push(new Vec3(holes[0].x - r, holes[0].y, holes[0].z - r));
    inholes.push(new Vec3(holes[1].x, holes[1].y, holes[1].z - r));
    inholes.push(new Vec3(holes[2].x + r, holes[2].y, holes[2].z - r));
    inholes.push(new Vec3(holes[3].x + r, holes[3].y, holes[3].z + r));
    inholes.push(new Vec3(holes[4].x, holes[4].y, holes[4].z + r));
    inholes.push(new Vec3(holes[5].x - r, holes[5].y, holes[5].z + r));
    return inholes;
  }
  public getDeskInHolePosition() {
    const r = 0.35;
    const holes = this.getDeskBallHolePosition();
    const inholes: Array<Vec3> = [];
    inholes.push(new Vec3(holes[0].x, holes[0].y, holes[0].z));
    inholes.push(new Vec3(holes[1].x, holes[1].y, holes[1].z - 2 * r)); 
    inholes.push(new Vec3(holes[2].x, holes[2].y, holes[2].z));
    inholes.push(new Vec3(holes[3].x, holes[3].y, holes[3].z));
    inholes.push(new Vec3(holes[4].x, holes[4].y, holes[4].z + 2 * r));
    inholes.push(new Vec3(holes[5].x, holes[5].y, holes[5].z));
    return inholes;
  }

  // 计算调整方向因子
  public calculateDirectionFactor() {
    const mainBall = this.getMainWhiteBall();
    if (this.currentHitBall && mainBall) {
      const distSqrt = (this.currentHitBall.worldPosition.x - mainBall.worldPosition.x) * (this.currentHitBall.worldPosition.x - mainBall.worldPosition.x)
        +  (this.currentHitBall.worldPosition.z - mainBall.worldPosition.z) * (this.currentHitBall.worldPosition.z - mainBall.worldPosition.z);
      if (distSqrt >= 25) {
        return 1.0
      }
      if (distSqrt <= 1) {
        return 1.0;
      }
      return 25 / distSqrt;
    }
    return 1.0;
  }
  public setCurrentHitBall(ball: Node) {
    this.currentHitBall = ball;
    if (ball) {
      this.currentHitPosition.set(ball.worldPosition);
    }
    return this.currentHitBall;
  }
  public getCurrentHitBall() {
    return this.currentHitBall;
  }
  public setCurrentHitPosition(pos: Vec3) {
    this.currentHitPosition.set(pos);
  }
  public getCurrentHitPosition() {
    return  this.currentHitPosition;
  }
  public setReflectVector(v: Vec3) {
    this.reflectVector.x = v.x;
    this.reflectVector.y = v.y;
    this.reflectVector.z = v.z;
  }
  public getReflectVector() {
    return this.reflectVector;
  }
  public setRefractVector(v: Vec3) {
    this.refractVector.x = v.x;
    this.refractVector.y = v.y;
    this.refractVector.z = v.z;
  }
  
  public getDeskComponentNames() {
    return ['DeskEdge', 'DeskTop', 'DeskCornerHole', 'DeskCenterHole'];
  }

  // 隐藏操作按钮
  public hideAllOperation() {
    const oc = this.node.getParent().getChildByName('OperationCanvas').getChildByName('UserOperateControl');
    const shc = this.node.getParent().getChildByName('OperationCanvas').getChildByName('SpinHitController');
    const force = oc.getChildByName('ForceControl');
    const direction = oc.getChildByName('DirectionControl');
    shc.active = false;
    force.active = false;
    direction.active = false;
  }
  // 显示操作按钮
  public showAllOperation() {
    const oc = this.node.getParent().getChildByName('OperationCanvas').getChildByName('UserOperateControl');
    const shc = this.node.getParent().getChildByName('OperationCanvas').getChildByName('SpinHitController');
    const force = oc.getChildByName('ForceControl');
    const direction = oc.getChildByName('DirectionControl');
    shc.active = true;
    force.active = true;
    direction.active = true;
  }
  public getRefractVector() {
    return this.refractVector;
  }
  
  public awakeAllBalls() {
    EventManager.Instance().Emit('AwakeMainWhiteBall', {});
    const balls = this.getActiveBalls();
    for (const ball of balls) {
      const body = ball.getComponent(RigidBody);
      const bNode = ball.getComponent(BallSphere);
      body.wakeUp();
      bNode.resetAngularDamping();
      body.type = ERigidBodyType.DYNAMIC;
    }
  }
  public sleepAllBalls() {
    EventManager.Instance().Emit('SleepMainWhiteBall', {});
    const balls = this.getActiveBalls();
    for (const ball of balls) {
      const body = ball.getComponent(RigidBody);
      const bNode = ball.getComponent(BallSphere);
      body.setAngularVelocity(Vec3.ZERO);
      body.clearState();
      bNode.setAngularDamping(1.0);
      body.type = ERigidBodyType.STATIC;
    }
  }
  public getWhiteBallPosition() {
    const pos = new Vec3(10, TableAtrribute.DeskTopAxisY, 0);
    const balls = this.getActiveBalls();
    const offsetz = [0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5];
    for (const z of offsetz) {
      pos.z = z;
      var hasCollision = false;
      for (const ball of balls) {
        const dist = (ball.worldPosition.x - pos.x) * (ball.worldPosition.x - pos.x)
          + (ball.worldPosition.z - pos.z) * (ball.worldPosition.z - pos.z);
        if (dist < 1.01) {
          hasCollision = true;
          break;
        }
      }
      if (!hasCollision) {
        return pos;
      }
    }
    pos.z = 0;
    return pos;
  }
  public getWhiteBallNearPosition() {
    const mbp = this.getWhiteBallPosition();
    const balls = this.getActiveBalls();
    const pt = new Vec3(0, TableAtrribute.DeskTopAxisY, 0);
    var minDist = 1000000000;
    for (const ball of balls) {
      const dist = (ball.worldPosition.x - mbp.x) * (ball.worldPosition.x - mbp.x)
        + (ball.worldPosition.z - mbp.z) * (ball.worldPosition.z - mbp.z);
      if (dist < minDist) {
        minDist = dist;
        pt.set(ball.worldPosition);
      }
    }
    return pt;
  }
}

